package myLearnjdk.jdk.nio.clientServer;

import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.io.IOException;
/**
 * nio 服务端
 * @author yangcheng  
 * @date 2020年8月14日  
 * @version V1.0
 */
public class ChargenServer {
    
  public static int DEFAULT_PORT = 19;
  
  public static void main(String[] args) {
  
    int port = 8081;
    
    System.out.println("Listening for connections on port " + port);

    byte[] rotation = new byte[95*2];
    for (byte i = ' '; i <= '~'; i++) {
      rotation[i -' '] = i;    
      rotation[i + 95 - ' '] = i;    
    }
    
    ServerSocketChannel serverChannel;
    Selector selector;
    try {
    /**
     * ServerSocketChannel的open方法只是用于实例化对象，并不会执行连接网络等操作
     */
      serverChannel = ServerSocketChannel.open();
      Set<SocketOption<?>> options = serverChannel.supportedOptions();
      for (SocketOption<?> socketOption : options) {
    	  /**
    	   * 输出为：
    	   *支持的option选项===SO_RCVBUF
			支持的option选项===SO_REUSEADDR
			支持的option选项===IP_TOS
    	   */
    	  
    	  System.out.println("支持的option选项==="+socketOption.toString());
      }
      ServerSocket ss = serverChannel.socket();
      InetSocketAddress address = new InetSocketAddress(port);
      ss.bind(address);
      serverChannel.configureBlocking(false);
      selector = Selector.open();
      /**
       * serverChannel将自己注册到selector中
       */
      serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    } catch (IOException ex) {
      ex.printStackTrace();
      return;   
    }
    
    while (true) {
      try {
		/**
		 * 阻塞,知道有通道就绪为止
		 */
        selector.select();
      } catch (IOException ex) {
        ex.printStackTrace();
        break;
      }
      /**
       * 获取所有就绪通道对应的key并用于
       * 遍历所有通道获取所有已经准备好的通道的对应的key
       */
      Set<SelectionKey> readyKeys = selector.selectedKeys();
      Iterator<SelectionKey> iterator = readyKeys.iterator();
      while (iterator.hasNext()) {
        
        SelectionKey key = iterator.next();
        /**
         * 删除当前key，告诉选择器当前key已经被处理过，下次执行selector.select()时只要
         * 对应的通道未就绪，就不会将该key放到就绪集合中了
         */
        iterator.remove();
        try {
		/**
		 * 服务器通道只准备接收
		 */
          if (key.isAcceptable()) {
            ServerSocketChannel server = (ServerSocketChannel) key.channel();
            SocketChannel client = server.accept();
            System.out.println("Accepted connection from " + client);
            client.configureBlocking(false);
            SelectionKey key2 = client.register(selector, SelectionKey.
                                                                    OP_WRITE);
            ByteBuffer buffer = ByteBuffer.allocate(74);
            buffer.put(rotation, 0, 72);
            buffer.put((byte) '\r');
            buffer.put((byte) '\n');
            buffer.flip();
            key2.attach(buffer);
           
          } else if (key.isWritable()) {
        	/**
        	 * 客户机通道只准备写入
        	 */
            SocketChannel client = (SocketChannel) key.channel();
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            if (!buffer.hasRemaining()) {
              // Refill the buffer with the next line
              buffer.rewind(); 
              // Get the old first character
              int first = buffer.get();
              // Get ready to change the data in the buffer
              buffer.rewind();
              // Find the new first characters position in rotation
              int position = first - ' ' + 1;
              // copy the data from rotation into the buffer
              buffer.put(rotation, position, 72);
              // Store a line break at the end of the buffer
              buffer.put((byte) '\r');
              buffer.put((byte) '\n');
              // Prepare the buffer for writing
              buffer.flip();
            }
            client.write(buffer);
          }
        } catch (IOException ex) {
          key.cancel();
          try {
            key.channel().close();
          }
          catch (IOException cex) {}
        }
      }
    }
  }
}